//
// TEST003.pde
// 
// Read pressure sensor data from Arduino via serial interface and draw chart.
// In current sketch, Arduino sends data every 15 minutes.
// format of data as follows
//      "xxxx.xx hpa yy.yy C"
//
//      written by elekid.
// 
// Processing site :  http://processing.org
// 

import processing.serial.*;

Serial port;
PFont myFont;

int cnt=0;

static int DATASIZE = 200;
float[] data = new float[DATASIZE];
String[] tag = new String[DATASIZE];

static int chartX = 50;
static int chartY = 10;
static int chartWidth = 400;
static int chartHeight = 320;

static int chartBackground = 240;
static int chartBackgroundNight = 215;

color dataColor = color(24, 24,128 );
color msgColor = color(255,0,0);
color lineColor = color(128);

float maxdata;
float mindata;
int intMaxdata;
int intMindata;


// for future use
// 現在未使用
static int midBaro=1000;
static int defaultMaxBaro=1020;
static int defaultMinBaro=990;

/**
 *   setup()
 */
void setup(){
  myFont = createFont("SansSerif", 18); 
  println(Serial.list());
  size (480,360);

  // Assuming that 3rd serial port is connected to Arduino
  port = new Serial(this, Serial.list()[2], 9600); 

  // Data will be available when NL(LF) receive.
  // LF文字受信でシリアルイベントが通知される
  port.bufferUntil(10);

  // draw() will be called every one second.
  // フレームレートを毎秒1フレームにする
  // CPU使用率を下げるため
  //frameRate(0.5);


  // DEBUG
  /*
   */
  addData(1006.51, "21:31");
  addData(1006.55, "21:33");
  addData(1006.52, "21:35");
  addData(1005.91, "21:36");
  addData(1005.92, "21:39");
  addData(1005.93, "21:39");
  addData(1006.57, "21:54");
  addData(1006.47, "22:09");
  addData(1006.46, "22:24");
  addData(1006.57, "22:39");
  addData(1006.43, "22:53");
  addData(1006.54, "23:08");
  addData(1006.38, "23:23");
  addData(1006.29, "23:38");
  addData(1006.47, "23:52");
  addData(1006.47, "23:53");
  addData(1006.40, "00:08");
  addData(1006.11, "00:23");
  addData(1006.10, "00:38");
  addData(1006.03, "00:53");
  addData(1006.06, "01:08");
  addData(1006.15, "01:23");
  addData(1005.69, "01:38");
  addData(1005.79, "01:53");
  addData(1005.78, "02:08");
  addData(1005.78, "02:22");
  addData(1005.84, "02:37");
  addData(1005.75, "02:52");
  addData(1005.53, "03:07");
  addData(1005.48, "03:22");
  addData(1005.64, "03:37");
  addData(1005.51, "03:52");
  addData(1005.47, "04:07");
  addData(1005.68, "04:22");
  addData(1005.62, "04:37");
  addData(1005.78, "04:52");
  addData(1006.04, "05:07");
  addData(1005.39, "05:22");
  addData(1005.71, "05:37");
  addData(1005.68, "05:52");
  addData(1006.36, "06:07");
  addData(1006.53, "06:22");
  addData(1006.70, "06:37");
  addData(1006.67, "06:51");
  addData(1006.76, "07:06");
  addData(1006.68, "07:21");
  addData(1006.77, "07:36");
  addData(1006.85, "07:51");
  addData(1006.91, "08:06");
  addData(1006.72, "08:21");
  addData(1006.59, "08:36");
  addData(1006.58, "08:51");
  addData(1006.59, "09:06");
  addData(1006.67, "09:21");
  addData(1006.65, "09:36");
  addData(1006.66, "09:51");
  addData(1006.57, "10:06");
  addData(1006.57, "10:21");
  addData(1006.57, "10:36");
  addData(1006.31, "10:51");
  addData(1006.08, "11:06");
  addData(1005.97, "11:20");
  addData(1005.89, "11:35");
  addData(1005.72, "11:50");
  addData(1005.64, "12:05");
  addData(1005.41, "12:20");
  addData(1005.17, "12:35");
  addData(1005.04, "12:50");
  addData(1005.00, "13:05");
  addData(1004.81, "13:20");
  addData(1004.70, "13:35");
  addData(1004.72, "13:50");
  addData(1004.81, "14:05");
  addData(1004.79, "14:20");
  addData(1004.72, "14:35");
  addData(1004.81, "14:50");
  addData(1004.77, "15:05");
  addData(1004.73, "15:20");
  addData(1004.74, "15:35");
  addData(1004.86, "15:49");
  addData(1004.73, "16:04");
  addData(1004.56, "16:19");
  addData(1004.59, "16:34");
  addData(1004.38, "16:49");
  addData(1004.33, "17:04");
  addData(1004.43, "17:19");
  addData(1004.48, "17:34");
  addData(1004.17, "17:49");
  addData(1004.41, "18:04");
  addData(1004.32, "18:19");
  addData(1003.63, "18:34");
  addData(1004.46, "18:49");
  addData(1004.47, "19:04");
  addData(1004.57, "19:19");
  addData(1004.61, "19:34");
  addData(1004.65, "19:49");
  addData(1004.78, "20:04");
  addData(1004.66, "20:19");
  addData(1004.75, "20:33");
  addData(1004.60, "20:48");
  addData(1004.76, "21:03");
  addData(1004.76, "21:18");
  addData(1004.66, "21:33");
  addData(1004.59, "21:48");
  addData(1004.64, "22:03");
  addData(1004.79, "22:18");
  addData(1004.72, "22:33");
  addData(1004.75, "22:48");
  addData(1004.57, "23:03");
  addData(1004.62, "23:18");
  addData(1004.14, "23:33");
  addData(1004.14, "23:48");
  addData(1004.14, "00:03");
  addData(1003.84, "00:18");
  addData(1003.89, "00:20");
  addData(1003.85, "00:24");
  addData(1003.95, "00:43");
  addData(1003.66, "00:58");
  addData(1003.39, "01:13");
  addData(1003.20, "01:27");
  addData(1003.19, "01:42");
  addData(1003.29, "01:57");
  addData(1003.09, "02:12");
  addData(1002.92, "02:27");
  addData(1003.02, "02:42");
  addData(1003.00, "02:57");
  addData(1002.96, "03:12");
  addData(1002.80, "03:27");
  addData(1002.99, "03:42");
  addData(1002.91, "03:57");
  addData(1002.82, "04:12");
  addData(1002.78, "04:27");
  addData(1002.73, "04:42");
  addData(1002.86, "04:57");
  /*
  addData(1003.01, "05:12");
   addData(1002.95, "05:27");
   addData(1003.02, "05:42");
   addData(1003.00, "05:56");
   addData(1002.97, "06:11");
   addData(1003.07, "06:26");
   addData(1003.07, "06:41");
   addData(1003.13, "06:56");
   addData(1003.09, "07:11");
   addData(1003.52, "07:22");
   addData(1003.53, "07:23");
   addData(1003.67, "07:27");
   addData(1003.79, "07:41");
   */
  // DEBUG END
}

/**
 *   draw()
 */
void draw(){
  background(210);
  smooth();

  // draw graph area
  noStroke();
  fill(chartBackground);
  rect( chartX, chartY, chartWidth, chartHeight);

  // colorize graph area 
  colorArea();

  // draw graph
  plotDataLabel();
  drawChart();

  // display current value  
  textFont(myFont, 18);
  fill(msgColor);
  text ("now : ",  170 , chartY + 20 );
  textFont(myFont, 24);
  text(int(data[0]), 215, chartY + 20);
  textFont(myFont, 18);
  text ("hPa",  275 , chartY + 20 );

}

/**
 *   serialEvent()
 *
 *   automatically called when Serial data is available.
 *   シリアルからのデータ受信時に自動的に呼び出される。
 */
void serialEvent(Serial p) { 

  String inString = (port.readString()); 
  saveLogFile(inString);

  String msg=String.format("%02d", hour())  + ":" +String.format("%02d", minute());
  String[] s=split(inString, " ");
  println(s[0] + " / " + msg);

  addData(float(s[0]), msg);

}

/**
 *   addData()
 *
 *   store sensor data into array
 *   シリアルで受信したセンサーのデータを配列にセットする
 *   配列の要素数は有限なので、古いものから上書きされて行く
 *   最新は要素0に保管
 */
void addData(float hpa, String msg) {

  cnt++;
  if ( cnt > DATASIZE) {
    cnt = DATASIZE;
  }

  // store latest data into data[0], 
  for ( int i=cnt-1 ; i > 0 ; i--){
    data[i]=data[i-1];
    tag[i] = tag[i-1];
  }
  data[0] =hpa;
  tag[0] = msg;

  maxdata = data[0] ; 
  mindata = data[0] ;

  for ( int i = 1 ; i < cnt ; i++) {
    if ( data[i] > maxdata ){
      maxdata = data[i];
    }
    if (data[i] < mindata ){
      mindata = data[i];
    }
  }
  intMindata  = int(mindata);
  intMaxdata = int(maxdata)+1;


}


/**
 *   drawChart()
 *
 *   draw line chart
 *   vertex()により折れ線グラフを描画する
 */
void drawChart(){
  stroke(dataColor);
  noFill();
  strokeWeight(2);

  beginShape();
  for (int i = 0 ; i <cnt ; i++){
    float x = getX(i);
    float y = getY(data[i]);
    vertex( x ,y );
    if ( i-1 == cnt || i == 0) {
      vertex( x ,y );
    }
  }
  endShape();

}

/**
 *   plotDataLabel()
 *
 *   plot x/y axis labels
 *   x軸及びy軸の描画、データラベルも
 */
void plotDataLabel(){
  textFont(myFont,10);
  for ( int v = intMindata ; v <= intMaxdata ; v++) {
    noFill();
    strokeWeight(1);
    stroke(lineColor);
    line ( chartX , getY(v), chartX+chartWidth, getY(v));
    fill(dataColor);
    text(v,chartX-30,getY(v)+5);

  }

  for ( int v = 0 ; v < DATASIZE ; v += 4) {
    noFill();
    strokeWeight(1);
    stroke(lineColor);
    line ( getX(v) , getY(intMaxdata), getX(v), getY(intMindata));
  }

  for ( int v = 0 ; v < DATASIZE ; v+=8) {
    if ( v >= cnt ) break;
    pushMatrix();
    translate(getX(v),getY(intMindata)+30);
    rotate(PI*3/2);
    fill(dataColor);
    text(tag[v],0,0);
    popMatrix();

  }
}

/*
 * getX()
 *
 *   calc x value
 *   描画の際の基準となるx値を計算する
 */
float getX (int cnt){
  float x = chartX + chartWidth - (cnt * chartWidth / DATASIZE);
  return x;
}

/*
 * getY()
 *
 *   calc y value
 *   描画の際の基準となるy値を計算する。
 */
float getY(float val){
  float y = chartY + (1 + intMaxdata - val) * chartHeight / (intMaxdata - intMindata+2);
  return y;
}

/*
 *  saveLogFile()
 *
 *  save sensor data into log file
 *  シリアルで受信したデータにタイムスタンプを付与してログファイルに保存する
 *  ファイルはスケッチの保存ディレクトリに作成される
 */
void saveLogFile(String outString) {
  String buffer = "[";
  buffer += String.valueOf(year());
  buffer += "/";
  buffer += String.valueOf(month());
  buffer += "/";
  buffer += String.valueOf(day());
  buffer += " ";
  buffer += String.format("%02d", hour());
  buffer += ":";
  buffer += String.format("%02d", minute());
  buffer += ":";
  buffer += String.format("%02d", second());
  buffer += "]  ";
  buffer += outString;

  // open existing file
  // 初回実行時にはゼロバイトでファイルを作成しておく必要がある
  String[] lines = loadStrings("sensor_data.log");
  String[] newlines= append( lines, buffer);

  // add one line and save
  saveStrings("sensor_data.log", newlines);
  print(buffer);
}
/* 
 * colorArea()
 *
 * 日付の変わり目の視認性を向上させるため
 * グラフの中で深夜 0:00 前後のエリアの背景色を暗くする
 */
void colorArea(){
  int [] MidNight = {
    0    };
  int pos=0;
  boolean foundFlg = false ; 
  boolean foundFlgPrev = false ; 

  for ( int i = 0 ; i < cnt ; i++){
    String[] s=split(tag[i], ":");
    // 0:xx found
    if (int(s[0]) == 0 ) {
      foundFlg=true;
      if ( MidNight.length < pos + 1) {
        MidNight = append(MidNight, 0);
      }
      // println("pos=" + pos);
      // println("  i=" + i);
      MidNight[pos] = i ;
    } 
    else {
      foundFlg=false;
    }
    if ( (foundFlg == false) && (foundFlgPrev == true) ) {
      pos++;
    }
    foundFlgPrev=foundFlg;
  }

  for ( int i = 0 ; i < MidNight.length ; i++ ){
    // datasize = 200
    // width= 400
    // --> 15 min = 2pixcel
    // --> 6 hour = 6 * 4 * 2= 48 pixcel
    // -->
    float mx = getX(MidNight[i]);
    if ( (mx -48 ) >= chartX  && ( mx + 48 <= chartX + chartWidth )) {
      for (int j = 0 ; j < 96 ; j++ ){
        if ( j < 10 ) {
          stroke(chartBackground - j* 2 );
        } 
        else if (j > 86 ) {
          stroke(chartBackground - (96-j)* 2 );
        } 
        else {
          stroke (chartBackgroundNight);
        }
        // line( x, y, x, y2 );
        line (mx - 48 + j, getY(intMaxdata), mx - 48 + j , getY(intMindata));
      }
    } 
    else if ( (mx -48 ) >= chartX  && ( mx  <= chartX + chartWidth )) {
      // println ("mx = " + mx);
      // println ("chartX + chartWidth - mx=" + (chartX + chartWidth - mx));
      for (int j = 0 ; j <= (48+(chartX + chartWidth - mx)) ; j++ ){
        if ( j < 10 ) {
          stroke(chartBackground - j* 2 );
        } 
        else {
          stroke (chartBackgroundNight);
        }
        // line( x, y, x, y2 );
        line (mx - 48 + j, getY(intMaxdata), mx - 48 + j , getY(intMindata));
      }
    } 
    else if  ( ( mx + 48 <= chartX + chartWidth ) &&  mx >= chartX) {
      // TODO
      for (int j=0 ; j <= (mx - chartX + 48) ; j++){
        float c = (mx - chartX + 48) - j;
        if ( c <= 10 ){
          stroke(chartBackground - c * 2 );
        } 
        else {
          stroke (chartBackgroundNight);
        }
        line (chartX + j, getY(intMaxdata), chartX + j , getY(intMindata));
      }

    }
  }
}


